//-----------------------------------------------------------------------
// <copyright file="ExceptionSerializer.cs" company="CodePlex">
//     Copyright (c) Doug Holland, CodePlex. All rights reserved.
// </copyright>
//-----------------------------------------------------------------------
namespace CodePlex.Diagnostics
{
    using System;
    using System.IO;
    using System.Text;
    using System.Diagnostics;

    using System.Runtime.Serialization;
    using System.Runtime.Serialization.Formatters.Soap;

    /// <summary>
    /// Provides the ability to serialize and deserialize classes derived from the <see cref="T:System.Exception"/> class.
    /// </summary>
    public static class ExceptionSerializer
    {
        #region public static string Serialize(Exception exception)

        /// <summary>
        /// Serializes the specified <see cref="T:System.Exception"/> instance to a Soap encoded message.
        /// </summary>
        /// <param name="exception">The <see cref="T:System.Exception"/> instance to serialize.</param>
        /// <returns>A <see cref="T:System.String"/> containing the Soap encoded <see cref="T:System.Exception"/> instance, or <c>null</c> if the supplied exception cannot be serialized.</returns>
        public static string Serialize(Exception exception)
        {
            if (exception == null)
            {
                throw new ArgumentNullException("exception");
            }

            Type type = exception.GetType();

            // ensure that the exception can be serialized, otherwise return null.
            if (!type.IsSerializable)
            {
                return null;
            }

            // ensure that the exception is not a generic type, the Soap serializer does not support generic types.
            if (type.IsGenericType)
            {
                return null;
            }

            #region Trace.WriteLine()

            Trace.Write("Serializing ");
            Trace.Write(exception.GetType().ToString());
            Trace.WriteLine(".");

            Trace.Flush();

            #endregion

            IFormatter formatter = new SoapFormatter() as IFormatter;

            using (MemoryStream stream = new MemoryStream())
            {
                // serialize the exception into a memory stream.
                formatter.Serialize(stream, exception);

                ASCIIEncoding encoding = new ASCIIEncoding();

                // reset the stream position to the beginning of the stream.
                stream.Seek(0, SeekOrigin.Begin);

                // read the stream into a byte array.
                byte[] buffer = new byte[stream.Length];
                stream.Read(buffer, 0, buffer.Length);

                // process the byte buffer into a string.
                return encoding.GetString(buffer);
            }
        }

        #endregion

        #region public static Exception Deserialize(string soap)

        /// <summary>
        /// Deserializes a <see cref="T:System.Exception"/> instance from the specified Soap encoded <see cref="T:System.String"/>. 
        /// </summary>
        /// <param name="soap">The <see cref="T:System.String"/> containing the Soap encoded <see cref="T:System.Exception"/> instance.</param>
        /// <returns>The deserialized <see cref="T:System.Exception"/> instance.</returns>
        public static Exception Deserialize(string soap)
        {
            if (string.IsNullOrEmpty(soap))
            {
                throw new ArgumentException(SR.GetString(SR.InvalidSoapEncodedException), "soap");
            }

            IFormatter formatter = new SoapFormatter() as IFormatter;

            using (MemoryStream stream = new MemoryStream())
            {
                ASCIIEncoding encoding = new ASCIIEncoding();

                // read the Soap encoded string into a byte array.
                byte[] buffer = encoding.GetBytes(soap);

                // write the byte array into the memory stream.
                stream.Write(buffer, 0, buffer.Length);

                // reset the stream position to the beginning of the stream.
                stream.Seek(0, SeekOrigin.Begin);

                // deserialize the exception from the memory stream.
                Exception exception = formatter.Deserialize(stream) as Exception;

                #region Trace.WriteLine()

                Trace.Write("Deserializing ");
                Trace.Write(exception.GetType().ToString());
                Trace.WriteLine(".");

                Trace.Flush();

                #endregion

                return exception;
            }
        }

        #endregion
    }
}

